using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Rensoft.CRM.Client.AccountsAPI;
using System.IO;
using Rensoft.CRM.Client.Properties;
using System.Diagnostics;
using Rensoft.Documents.Accounts.Invoice;

namespace Rensoft.CRM.Client.Editors
{
    public partial class InvoiceSalesEditor : BaseEditor
    {
        //private bool contactsUpdatePending;
        private Invoice invoice;
        private Account account;
        private string vat;
        private ToolStripSeparator toolStripSeparator;
        private ToolStripButton createDocumentButton;
        private ClientAccountSnapshot accountSnapshot;

        protected delegate Invoice CreateInvoiceMethod();

        protected decimal VatPercent
        {
            get
            {
                switch (vat)
                {
                    case "17.5%":
                        return 0.175m;

                    default:
                        return 0m;
                }
            }
        }

        protected decimal VatAmount
        {
            get { return SubtotalAmount * VatPercent; }
        }

        protected decimal SubtotalAmount
        {
            get
            {
                decimal subtotal = 0;
                foreach (DataGridViewRow row in itemsDataGridView.Rows)
                {
                    if (row.Cells["TotalColumn"].Value != null)
                    {
                        subtotal += (decimal)row.Cells["TotalColumn"].Value;
                    }
                }
                return subtotal;
            }
        }

        protected decimal TotalAmount
        {
            get { return SubtotalAmount + VatAmount; }
        }

        protected decimal OtherCharges
        {
            get
            {
                return accountSnapshot.InvoicesToday;
            }
        }

        protected decimal PreviousAccountBalance
        {
            get
            {
                return accountSnapshot.PreviousBalance;
            }
        }

        /// <summary>
        /// Gets the balance inclusive to the current invoice.
        /// </summary>
        protected decimal TotalAccountBalance
        {
            get
            {
                return accountSnapshot.PreviousBalance +
                    accountSnapshot.InvoicesExclusive +
                    PaymentsAndCredits + TotalAmount;
            }
        }

        protected decimal PaymentsAndCredits
        {
            get
            {
                return accountSnapshot.PaymentsToday + accountSnapshot.CreditsToday;
            }
        }

        public InvoiceSalesEditor(Program program) : base(program)
        {
            InitializeComponent();
            
            SetMode(EditorMode.Create);
            Text = "New Sales Invoice - " + AssemblyInfo.Title;

            toolStripSeparator = new ToolStripSeparator();
            createDocumentButton = new ToolStripButton("Create PDF", Resources.New);
            createDocumentButton.Click += new EventHandler(createDocumentButton_Click);

            AddToolStripItem(toolStripSeparator, true);
            AddToolStripItem(createDocumentButton, true);
        }

        public InvoiceSalesEditor(Program program, Invoice invoice)
            : this(program)
        {
            this.invoice = invoice;
            SetMode(EditorMode.Modify);
            Text = "Client Invoice " + invoice.InvoiceRef + " - " + AssemblyInfo.Title;
        }

        protected override void PerformClear()
        {
            base.PerformClear();

            /* Intentionally leave date the same.
             * Currency automatically resets. */

            clientRefTextBox.ResetText();
            itemsDataGridView.Rows.Clear();
        }

        protected override void SaveAsync(SaveMode mode)
        {
            // Disable fields until save complete.
            invoiceNumberTextBox.Enabled = false;
            clientComboBox.Enabled = false;
            itemsDataGridView.Enabled = false;
            clientRefTextBox.Enabled = false;
            contactsComboBox.Enabled = false;
            dateTimePicker.Enabled = false;
            vatComboBox.Enabled = false;

            base.SaveAsync(mode);
        }

        protected override void OnSaveComplete(object sender, EventArgs e)
        {
            base.OnSaveComplete(sender, e);

            invoiceNumberTextBox.Enabled = true;
            clientComboBox.Enabled = true;
            itemsDataGridView.Enabled = true;
            clientRefTextBox.Enabled = true;
            dateTimePicker.Enabled = true;
            vatComboBox.Enabled = true;
        }

        protected delegate void UpdateInvoiceNumber(string value);
        private void updateInvoiceNumber(string value)
        {
            invoiceNumberTextBox.Text = value;
        }

        protected delegate void UpdateSnapshot();
        private void updateSnapshot()
        {
            loadAccountSnapshot(account.AccountId);
            updateTotalAmountLabels();
        }

        protected override void PerformLoad(DoWorkEventArgs e)
        {
            base.PerformLoad(e);

            InitializeAccountComboBox(clientComboBox, AccountType.Client);
            AccountsService accountsService = AccountsService.CreateInstance();

            switch (Mode)
            {
                case EditorMode.Create:
                    {
                        if (accountsService.SettingExists("NextClientInvoiceNumber"))
                        {
                            string value = accountsService.SettingFetch("NextClientInvoiceNumber");
                            if (InvokeRequired)
                            {
                                UpdateInvoiceNumber invoker = new UpdateInvoiceNumber(updateInvoiceNumber);
                                Invoke(invoker, value);
                            }
                            else
                            {
                                updateInvoiceNumber(value);
                            }
                        }
                    }
                    break;

                case EditorMode.Modify:
                    {
                        SaveToolStripButton.Enabled = false;
                        SaveNewToolStripButton.Enabled = false;
                        SaveCloseToolStripButton.Enabled = false;

                        dateTimePicker.Value = invoice.Date;
                        invoiceNumberTextBox.Text = invoice.InvoiceRef;
                        clientRefTextBox.Text = invoice.RecipientRef;

                        InvoiceItem[] itemArray = accountsService.InvoiceItemFetch(invoice.InvoiceId);
                        foreach (InvoiceItem invoiceItem in itemArray)
                        {
                            object[] rowArray = new object[] {
                                invoiceItem.Details,
                                invoiceItem.Quantity,
                                invoiceItem.UnitType.ToString(),
                                invoiceItem.Rate,
                                (invoiceItem.Rate * (decimal)invoiceItem.Quantity)
                            };

                            itemsDataGridView.Rows.Add(rowArray);
                        }
                    }
                    break;
            }

            if (InvokeRequired)
            {
                UpdateSnapshot snapshotInvoker = new UpdateSnapshot(updateSnapshot);
                Invoke(snapshotInvoker);
            }
            else
            {
                updateSnapshot();
            }
        }

        protected override void OnLoadComplete(object sender, EventArgs e)
        {
            if (Mode == EditorMode.Modify)
            {
                // Select the client from the list where account ID matches.
                foreach (AccountComboItem item in clientComboBox.Items)
                {
                    if (item.Value.AccountId == invoice.AccountId)
                    {
                        clientComboBox.SelectedItem = item;
                        break;
                    }
                }
            }

            //updateContactsAsync();

            // Always select "None" by default.
            vatComboBox.SelectedIndex = 0;

            // Datagrid was staying on UseWaitCursor for some reason?
            itemsDataGridView.Cursor = Cursors.Default;

            base.OnLoadComplete(sender, e);
        }

        protected override void PerformSave(DoWorkEventArgs e)
        {
            base.PerformSave(e);

            switch (Mode)
            {
                case EditorMode.Create:
                    {
                        // Create invoice on same thread as form to access controls.
                        CreateInvoiceMethod createInvoiceMethod = new CreateInvoiceMethod(createInvoice);
                        Invoice invoice = (Invoice)Invoke(createInvoiceMethod);
                    }
                    break;
            }
        }

        void createDocumentButton_Click(object sender, EventArgs e)
        {
            // Makes sure any changes are applied, even when in edit mode.
            itemsDataGridView.EndEdit();
            CreateDocument();
        }

        void itemsDataGridView_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            throw e.Exception;
        }

        private Invoice createInvoice()
        {
            AccountsService accountsService = new AccountsService();
            accountsService.UseDefaultCredentials = true;

            InvoiceItem[] items = getInvoiceItems().ToArray();
            int accountId = account.AccountId;

            Invoice invoice = accountsService.InvoiceCreate(
                invoiceNumberTextBox.Text, clientRefTextBox.Text,
                items, dateTimePicker.Value, InvoiceType.Client, accountId);

            // Increment invoice number for next time.
            int nextNumber = int.Parse(invoiceNumberTextBox.Text) + 1;
            accountsService.SettingUpdate("NextClientInvoiceNumber", nextNumber.ToString());

            return invoice;
        }

        private void clientComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            AccountComboItem item = (AccountComboItem)clientComboBox.SelectedItem;
            account = item.Value;

            if (!Loading)
            {
                //updateContactsAsync();
                loadAccountSnapshotAsync();
                updateCurrency();
            }
        }

        private void updateCurrency()
        {
            currencyTextBox.Text = account.Currency;
        }

        /*protected void updateContactsAsync()
        {
            if (contactsLoadBackgroundWorker.IsBusy)
            {
                // Set to pending, and update later.
                contactsUpdatePending = true;
                return;
            }

            contactsUpdatePending = false;
            Cursor = Cursors.WaitCursor;
            createDocumentButton.Enabled = false;
            contactsComboBox.Enabled = false;
            contactsComboBox.Items.Clear();
            contactsLoadBackgroundWorker.RunWorkerAsync(account.AccountId);
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            contactsLoadBackgroundWorker.CancelAsync();
            base.OnClosing(e);
        }

        private void contactsLoadBackgroundWorker_RunWorkerCompleted(
            object sender, RunWorkerCompletedEventArgs e)
        {
            if (contactsUpdatePending)
            {
                updateContactsAsync();
                return;
            }

            Cursor = Cursors.Default;

            if (contactsComboBox.Items.Count != 0)
            {
                createDocumentButton.Enabled = true;
                contactsComboBox.Enabled = true;
                contactsComboBox.SelectedIndex = 0;
            }
        }*/

        private void updateTotalAmountLabels()
        {
            invoiceTotalLabel.Text = TotalAmount.ToString("C");
            totalBalanceLabel.Text = TotalAccountBalance.ToString("C");
            subtotalAmountLabel.Text = SubtotalAmount.ToString("C");
            totalAmountLabel.Text = TotalAmount.ToString("C");
            previousBalanceLabel.Text = PreviousAccountBalance.ToString("C");
            otherChargesLabel.Text = OtherCharges.ToString("C");
            paymentsCreditsLabel.Text = PaymentsAndCredits.ToString("C");
        }

        private List<InvoiceItem> getInvoiceItems()
        {
            List<InvoiceItem> itemsList = new List<InvoiceItem>();
            foreach (DataGridViewRow row in itemsDataGridView.Rows)
            {
                if ((row.Cells["DetailsColumn"].Value != null) &&
                    (row.Cells["RateColumn"].Value != null))
                {
                    InvoiceItem item = new InvoiceItem();

                    item.Details = row.Cells["DetailsColumn"].Value.ToString();
                    item.Rate = decimal.Parse(row.Cells["RateColumn"].Value.ToString());

                    // Default quantity to none.
                    item.Quantity = 1;
                    if (row.Cells["QtyColumn"].Value != null)
                    {
                        string stringQty = row.Cells["QtyColumn"].Value.ToString().Trim();
                        if (!string.IsNullOrEmpty(stringQty))
                        {
                            item.Quantity = float.Parse(stringQty);
                        }
                    }

                    switch ((string)row.Cells["UnitsColumn"].Value)
                    {
                        case "Hours":
                            item.UnitType = UnitType.Hours;
                            break;

                        case "Days":
                            item.UnitType = UnitType.Days;
                            break;

                        case "Months":
                            item.UnitType = UnitType.Months;
                            break;

                        case "Years":
                            item.UnitType = UnitType.Years;
                            break;

                        default:
                            item.UnitType = UnitType.Other;
                            break;
                    }

                    itemsList.Add(item);
                }
            }
            return itemsList;
        }

        private void itemsDataGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex != -1)
            {
                DataGridViewRow row = itemsDataGridView.Rows[e.RowIndex];
                bool qtyColumnUpdated = e.ColumnIndex == row.Cells["QtyColumn"].ColumnIndex;
                bool rateColumnUpdated = e.ColumnIndex == row.Cells["RateColumn"].ColumnIndex;

                // Only update if either have changed, and neither are null.
                if ((rateColumnUpdated | qtyColumnUpdated) &&
                    (row.Cells["RateColumn"].Value != null))
                {
                    decimal rate = decimal.Parse(row.Cells["RateColumn"].Value.ToString());

                    float quantity = 1;
                    if (row.Cells["QtyColumn"].Value != null)
                    {
                        string stringQty = row.Cells["QtyColumn"].Value.ToString().Trim();
                        if (!string.IsNullOrEmpty(stringQty))
                        {
                            quantity = float.Parse(stringQty);
                        }
                    }

                    row.Cells["TotalColumn"].Value = rate * (decimal)quantity;
                    updateTotalAmountLabels();
                }
            }
        }

        private void vatComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            vat = (string)vatComboBox.SelectedItem;
            updateTotalAmountLabels();
        }

        public void CreateDocument()
        {
            this.UseWaitCursor = true;

            PdfInvoiceDetails details = new PdfInvoiceDetails();
            details.ClientCode = account.Code;
            details.InvoiceNumber = invoiceNumberTextBox.Text;
            details.InvoiceDate = dateTimePicker.Value.ToShortDateString();
            details.InvoiceCurrency = account.Currency;
            details.InvoiceTotal = TotalAmount.ToString("C");
            details.InvoiceSubtotal = SubtotalAmount.ToString("C");
            details.VatAmount = VatAmount.ToString("C");
            details.PaymentTerms = "Due on Reciept";
            details.ClientCompanyName = account.Title;
            details.AccountPreviousBalance = PreviousAccountBalance.ToString("C");
            details.AccountTotalBalance = TotalAccountBalance.ToString("C");
            details.PaymentsAndCredits = PaymentsAndCredits.ToString("C");
            details.OtherChargesAndInvoices = OtherCharges.ToString("C");
            details.ClientReference = clientRefTextBox.Text;

            foreach (InvoiceItem item in getInvoiceItems())
            {
                PdfInvoiceItem pdfItem = new PdfInvoiceItem();

                pdfItem.Details = item.Details;
                pdfItem.Quantity = item.Quantity.ToString("N2");
                pdfItem.Rate = item.Rate.ToString("N2");
                pdfItem.Total = ((decimal)item.Quantity * item.Rate).ToString("N2");
                pdfItem.Units = item.UnitType.ToString();

                details.ItemList.Add(pdfItem);
            }

            PdfInvoice pdfInvoice = new PdfInvoice(
                Settings.Default.InvoiceTemplate, details);

            string fileName = string.Format(
                Settings.Default.InvoiceFilenameFormat,
                invoiceNumberTextBox.Text, account.Code);

            pdfSaveFileDialog.InitialDirectory = Settings.Default.InvoiceDirectory;
            pdfSaveFileDialog.FileName = fileName;

            UseWaitCursor = false;
            if (pdfSaveFileDialog.ShowDialog() == DialogResult.OK)
            {
                UseWaitCursor = true;
                pdfBackgroundWorker.RunWorkerAsync(pdfInvoice);
            }
        }

        private void loadAccountSnapshotAsync()
        {
            if (!snapshotBackgroundWorker.IsBusy)
            {
                snapshotBackgroundWorker.RunWorkerAsync(account.AccountId);
            }
        }

        private void loadAccountSnapshot(int accountId)
        {
            AccountsService accountsService = new AccountsService();
            accountsService.UseDefaultCredentials = true;

            // Allow invoice ID to be invalid so no exclusions are made.
            int invoiceId = -1;
            if (Mode == EditorMode.Modify)
            {
                invoiceId = invoice.InvoiceId;
            }

            accountSnapshot = accountsService.ClientAccountSnapshotFetch(
                accountId, invoiceId, dateTimePicker.Value);
        }

        private void snapshotBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            loadAccountSnapshot((int)e.Argument);
        }

        private void snapshotBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                throw e.Error;
            }

            updateTotalAmountLabels();
        }

        private void dateMonthCalendar_DateChanged(object sender, DateRangeEventArgs e)
        {
            if (!Loading)
            {
                Invoke(new MethodInvoker(loadAccountSnapshotAsync));
            }
        }

        private void pdfBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (e.Argument is PdfInvoice)
            {
                PdfInvoice pdfInvoice = e.Argument as PdfInvoice;
                pdfInvoice.Save(pdfSaveFileDialog.FileName);
                Process.Start(pdfSaveFileDialog.FileName);
            }
            else
            {
                throw new InvalidOperationException(
                    "PDF background worker was started with an argument type other than PdfInvoice.");
            }
        }

        private void pdfBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                throw e.Error;
            }

            UseWaitCursor = false;
        }
    }
}